import socket
import threading
import json
import pymysql
import struct
import select
import os
import io
import time
import printCurrentTime as pct
from PIL import Image, UnidentifiedImageError
# 全局变量
connections = []
connections_lock = threading.Lock()

def calculate_crc(data):
    crc = 0xFFFF
    for pos in data:
        crc ^= pos
        for _ in range(8):
            if (crc & 0x0001) != 0:
                crc >>= 1
                crc ^= 0xA001
            else:
                crc >>= 1
    return crc

def append_crc(data):
    crc = calculate_crc(data)
    data.append(crc & 0xFF)
    data.append((crc >> 8) & 0xFF)
    return data

def get_command_by_register(register, config):
    try:
        return config['commands'][register]
    except KeyError:
        raise ValueError("Unknown register")

def load_config(filename):
    with open(filename, 'r') as f:
        config = json.load(f)
    return config

def connect_to_database(config):
    return pymysql.connect(
        host=config['host'],
        port=config['port'],
        user=config['user'],
        password=config['password'],
        db=config['db'],
        charset=config['charset']
    )
def read_image_data(client_socket,register_byte):
    router_number = '{:04x}'.format(register_byte)                 #format(register_byte, '02x')
    sensor_number = router_number + '08'
    try:
        # Step 1: 发送拍照指令
        take_photo_command = struct.pack('>BBBBB', 0x56, 0x08, 0x36, 0x01, 0x00)
        client_socket.send(take_photo_command)
        pct.curtime()
        print('拍照指令发送:', take_photo_command)

        # Step 2: 接收拍照成功返回
        timeout = 50  # 设置短超时，用于接收拍照成功返回
        readable, _, _ = select.select([client_socket], [], [], timeout)

        if not readable:
            pct.curtime()
            print("未在指定时间内收到拍照成功确认.")
            return
        else:
            response = client_socket.recv(512)
            print(response)
            if response == b'\x76\x08\x36\x00\x00':
                pct.curtime()
                print("拍照成功")
            else:
                pct.curtime()
                print("拍照失败或返回值不正确")
                return
        # Step 3: 发送读取图片长度指令
        get_image_size_command = struct.pack('>BBBBB', 0x56, 0x08, 0x34, 0x01, 0x00)
        client_socket.send(get_image_size_command)
        pct.curtime()
        print('读取图片长度指令发送:', get_image_size_command)
        # 接收图片长度
        readable, _, _ = select.select([client_socket], [], [], timeout)
        if not readable:
            pct.curtime()
            print("未在指定时间内收到图片长度信息.")
            return
        else:
            response = client_socket.recv(512)
            if response[:5] == b'\x76\x08\x34\x00\x04':
                image_length = struct.unpack('>I', response[5:9])[0]
                pct.curtime()
                print(f"图片长度: {image_length} bytes")
            else:
                pct.curtime()
                print("读取图片长度失败或返回值不正确")
                return
        # Step 4: 动态构建读取图片数据的指令
        image_length_bytes = struct.pack('>I', image_length)
        get_image_command = struct.pack('>BBBBBBBBBBBBBBBB',
                                        0x56, 0x08, 0x32, 0x0C, 0x00, 0x0A,
                                        0x00, 0x00, 0x00, 0x00,
                                        image_length_bytes[0], image_length_bytes[1],
                                        image_length_bytes[2], image_length_bytes[3],
                                        0x00, 0xFF)

        client_socket.send(get_image_command)
        pct.curtime()
        print('读取图片数据指令发送:', get_image_command)
        # 接收图片数据
        buffer = b''
        while True:
            data = client_socket.recv(512)
            if not data:
                pct.curtime()
                print("数据传输完毕")
                break
            buffer += data
            # 如果接收到的数据长度达到预期，或检测到结束标志，可以停止接收
            if len(buffer) >= image_length+10:
                break
        #print(buffer)
        pct.curtime()
        print(f"Total data received from client ({len(buffer)} bytes)")
        # 查找 JPEG 文件的起始和结束标记
        start_marker = b'\xff\xd8'  # JPEG 起始标记
        end_marker = b'\xff\xd9'  # JPEG 结束标记
        start_index = buffer.find(start_marker)
        end_index = buffer.find(end_marker)
        if start_index == -1 or end_index == -1:
            pct.curtime()
            print("JPEG 文件标记未找到。")
        else:
            # 包含结束标记的范围
            end_index += len(end_marker)
            cropped_buffer = buffer[start_index:end_index]
            # 确保保存的文件夹存在
            save_folder = 'C:\\picture'
            os.makedirs(save_folder, exist_ok=True)
            # 使用时间戳生成唯一文件名
            timestamp = time.strftime('%Y%m%d_%H%M%S')
            image_filename = f'received_image_{timestamp}.jpg'
            image_path = os.path.join(save_folder, image_filename)
            # 尝试将数据加载为图像
            try:
                image = Image.open(io.BytesIO(cropped_buffer))
                #image.show()  # 显示图像，确保数据有效
                image.save(image_path)  # 保存为图片文件
                pct.curtime()
                print(f"Image saved successfully to {image_path}.")
            except UnidentifiedImageError:
                pct.curtime()
                print("无法识别图像文件。数据可能无效或格式不正确。")
            except Exception as e:
                pct.curtime()
                print(f"发生错误：{e}")
    except Exception as e:
        pct.curtime()
        print(f"An error occurred while handling client connection: {e}")
    return image_path,router_number, sensor_number

def insert_image_path(db_connection, image_path,router_number, sensor_number):
    cursor = db_connection.cursor()
    local_time = time.strftime("%Y%m%d%H%M%S", time.localtime())

    sql = """
    INSERT INTO sensor_data (timestamp, router_number, sensor_number,air_humidity, air_temperature,humidity, temperature, conductivity, ph, nitrogen, phosphorus, potassium,picture_path)
    VALUES (%s, %s, %s,%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
    """
    values = (
        local_time,
        router_number,
        sensor_number,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        image_path
    )
    cursor.execute(sql, values)
    db_connection.commit()
    cursor.close()

def process_soil_sensor_data(response, register_byte):
    hex_response = response.hex()
    router_number = '{:04x}'.format(register_byte)            #format(register_byte, '02x')
    sensor_number = router_number + hex_response[0:2]

    pct.curtime()
    print("Response (bytes):", response)
    pct.curtime()
    print("Response (hex):", response.hex())

    humidity_value = int.from_bytes(response[3:5], byteorder='big', signed=False) / 10.0
    temperature_value = int.from_bytes(response[5:7], byteorder='big', signed=True) / 10.0
    conductivity_value = int.from_bytes(response[7:9], byteorder='big', signed=False)
    ph_value = int.from_bytes(response[9:11], byteorder='big', signed=False) / 10.0
    nitrogen_value = int.from_bytes(response[11:13], byteorder='big', signed=False)
    phosphorus_value = int.from_bytes(response[13:15], byteorder='big', signed=False)
    potassium_value = int.from_bytes(response[15:17], byteorder='big', signed=False)
    sensor_data = {
        "humidity": humidity_value,
        "temperature": temperature_value,
        "conductivity": conductivity_value,
        "ph": ph_value,
        "nitrogen": nitrogen_value,
        "phosphorus": phosphorus_value,
        "potassium": potassium_value
    }

    return sensor_data, router_number, sensor_number

def insert_soil_sensor_data(db_connection, sensor_data, router_number, sensor_number):
    cursor = db_connection.cursor()
    local_time = time.strftime("%Y%m%d%H%M%S", time.localtime())

    sql = """
    INSERT INTO sensor_data (timestamp, router_number, sensor_number,air_humidity, air_temperature,humidity, temperature, conductivity, ph, nitrogen, phosphorus, potassium, picture_path)
    VALUES (%s, %s, %s,%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
    """
    values = (
        local_time,
        router_number,
        sensor_number,
        0,  # air_humidity，如果sensor_data中没有这个键
        0,  # air_temperature，如果sensor_data中没有这个键
        sensor_data['humidity'],
        sensor_data['temperature'],
        sensor_data['conductivity'],
        sensor_data['ph'],
        sensor_data['nitrogen'],
        sensor_data['phosphorus'],
        sensor_data['potassium'],
        0
    )
    cursor.execute(sql, values)
    db_connection.commit()
    cursor.close()

def handle_client(client_socket, register_byte, config):
    database_config = config['database_config']
    db_connection = connect_to_database(database_config)
    try:
        register_hex = '{:04x}'.format(register_byte)     #format(register_byte, '02x')
        pct.curtime()
        print(f"Received register in hex: {register_hex}")
        commands = get_command_by_register(register_hex, config)
        print(commands)
        for command in commands:
            try:
                first_command_first_byte_hex = '{:04x}'.format(command[0])    #format(command[0], '02x')
                combined_key = register_hex + first_command_first_byte_hex
                register_functions = config['register_functions'].get(combined_key, {})
                process_function_name = register_functions.get("process_function")
                insert_function_name = register_functions.get("insert_function")

                process_function = globals().get(process_function_name) if process_function_name else None
                insert_function = globals().get(insert_function_name) if insert_function_name else None

                # 获取图片处理标记
                needs_image_processing = False
                if len(command) > 6:
                    needs_image_processing = command[6]

                command_copy = command[:6]  # 只取前6个元素用于CRC计算
                command_with_crc = append_crc(command_copy)

                client_socket.sendall(bytearray(command_with_crc))
                pct.curtime()
                print("3")

                timeout = 10
                readable, _, _ = select.select([client_socket], [], [], timeout)
                if not readable:
                    pct.curtime()
                    print("No data received from client within the timeout period.")
                else:
                    try:
                        response = client_socket.recv(1024)
                        if not response:
                            pct.curtime()
                            print("Client closed the connection.")
                        else:
                            pct.curtime()
                            print("Data received from client:", response)
                    except Exception as e:
                        pct.curtime()
                        print(f"Exception occurred while receiving data: {e}")

                pct.curtime()
                print("4")
                if len(response) > 1:
                    pct.curtime()
                    print(f"Received complete response for command {command}: {response.hex()}")

                    # 检查是否需要进行图片处理
                    if needs_image_processing:
                        try:
                            image_path, router_number, sensor_number = read_image_data(client_socket, register_byte)
                            insert_image_path(db_connection, image_path, router_number, sensor_number)
                            pct.curtime()
                            print(f"Image processed and inserted: {image_path}")
                        except Exception as e:
                            pct.curtime()
                            print(f"Error during image processing: {e}")

                    if process_function and insert_function:
                        try:
                            sensor_data, router_number, sensor_number = process_function(response, register_byte)
                            insert_function(db_connection, sensor_data, router_number, sensor_number)
                            pct.curtime()
                            print("Data inserted into database:", sensor_data, router_number, sensor_number)
                        except Exception as e:
                            pct.curtime()
                            print(f"Error during processing or inserting data: {e}")
                    else:
                        pct.curtime()
                        print(f"Response without processing: {response.hex()}")
                        continue

                pct.curtime()
                print("2")
            except Exception as e:
                print(f"An error occurred while processing command {command}: {e}")
    except Exception as e:
        pct.curtime()
        print(f"Error: {e}")
    finally:
        if db_connection is not None:
            db_connection.close()

def poll_clients(config):
    while True:
        start_time = time.time()
        with connections_lock:
            if not connections:
                pct.curtime()
                print("No client connected. Waiting 10s.")
                time.sleep(10)
                continue
            for client_socket, register_byte in connections:
                handle_client(client_socket, register_byte, config)
        elapsed_time = time.time() - start_time
        pct.curtime()
        print(f"One loop took {elapsed_time} seconds to process.")
        wait_time = config['wait_time']
        pct.curtime()
        print(f"Waiting for {wait_time / 60} minutes before processing clients again")
        time.sleep(wait_time)

def accept_connections(server_socket):
    while True:
        client_socket, addr = server_socket.accept()
        pct.curtime()
        print(f"Accepted connection from {addr}")

        register_byte = client_socket.recv(1)[0]
        print (register_byte)



        register_byte = data  # 例如 "0102"
        print(register_byte)  # 输出 "0102"


        with connections_lock:
            connections.append((client_socket, register_byte))

def main():
    try:
        config = load_config('config.json')
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # server_socket.settimeout(50)
        server_socket.bind((config['server_ip'], config['server_port']))
        server_socket.listen(128)
        pct.curtime()
        print(f"Server is listening on {config['server_ip']}:{config['server_port']}...")
        accept_thread = threading.Thread(target=accept_connections, args=(server_socket,))
        accept_thread.start()
        poll_thread = threading.Thread(target=poll_clients, args=(config,))
        poll_thread.start()
    except Exception as e:
        pct.curtime()
        print(f"An error occurred in main: {e}")

if __name__ == "__main__":
    main()
